--[[
params.xx = {  xx为容器布局的名称，用于在container组件中设置打开的容器布局
    widget = {容器UI界面相关的所有配置
        slotpos:
                必选，表类型，其中每个键值都是Vector3数组，代表容器中每个物品槽的位置坐标
                槽位数量由数组长度决定，坐标原点通常在容器UI中心
                举例：
                    slotpos = {
                                Vector3(-162, -75 * 0 + 114, 0),  -- 第1个槽位
                                Vector3(-162 + 75, -75 * 0 + 114, 0),  -- 第2个槽位
                            }
        slotbg：
                可选，表类型，其中每个键值都是一个表，代表每个槽位的背景图像,与slotpos一一对应
                举例：
                    slotbg = {
                                { image = "tsqt_params_00.tex", atlas = "images/containerimages/tsqt_params_00.xml" },
                                { image = "tsqt_params_00.tex", atlas = "images/containerimages/tsqt_params_00.xml" },
                            }
        animbank：
                必选，字符串，容器动画的bank，即容器打开关闭以及常规状态下的动画，一般都是用游戏中现成的
                举例：
                    animbank = "ui_cookpot_1x2",
        animbuild：
                必选，字符串，容器动画的build，即容器布局的材质，用于更改容器布局的外观
                举例：
                    animbuild = "tsqt_params_00",
        bgatlas：
                可选，字符串，指定背景图像所在的图集文件路径，必须与 bgimage 同时使用才生效
                注意，bgatlas/bgimage与animbank/animbuild是互斥的，即使都设置了，也是bgatlas优先
                举例：
                    bgatlas = "images/containerimages/tsqt_params_00.xml",
        bgimage：
                可选，字符串，指定背景图像的文件名，必须与 bgatlas 同时使用才生效
                设置了以上两个属性后，容器的背景（即格子下面的背景）会变成静态图片，而不是原版官方带open和close的动画
                举例：
                    bgimage = "tsqt_params_00.tex",
        animbank_upgraded：
                可选，字符串，容器升级后的动画bank，即容器升级后的动画，一般都是用游戏中现成的
                举例：
                    animbank_upgraded = "ui_cookpot_1x2_upgraded",
        animbuild_upgraded：
                可选，字符串，容器升级后的动画build，即容器升级后的动画
                举例：
                    animbuild_upgraded = "tsqt_params_00_upgraded",
        pos：
                必选，Vector3数组，容器UI在屏幕上的显示位置，经测试，(0,0)位置就是被添加对象的位置，比如这里是把这个容器添加到阿比盖尔身上，所以容器出现位置的原点就是阿比盖尔所在位置，左上为正，右下为负
                举例：
                    Vector3(0, 200, 0) - 被添加对象中央偏上
                    Vector3(-5, -80, 0) - 被添加对象左侧偏下
                    Vector3(200, 0, 0) - 被添加对象右侧中央
        side_align_tip：
                可选，number，侧边对齐提示的距离,影响容器打开时与其他UI元素的对齐距离
                举例：
                    side_align_tip = 100,
        top_align_tip：
                可选，number，顶部对齐提示的距离
                举例：
                    top_align_tip  = 50，
        bottom_align_tip：
                可选，number，底部对齐提示的距离
                举例：
                    bottom_align_tip = -100，
        buttoninfo：
                可选，表类型，容器按钮（如烹饪按钮）的配置
                含有以下子选项：
                buttoninfo = 
                {
                    text = STRINGS.ACTIONS.COOK,        -- 按钮文本
                    position = Vector3(0, -165, 0),     -- 按钮位置
                    fn = function(inst, doer)           -- 点击执行函数
                        -- 按钮逻辑
                    end,
                    validfn = function(inst)            -- 按钮可用性验证
                        return inst.replica.container:IsFull()
                    end
                }
        opensound:
                可选，字符串，容器打开时的声音
                举例：
                    opensound = "ui_cookpot_1x2_open",
        closesound:
                可选，字符串，容器关闭时的声音
                举例：
                    closesound = "ui_cookpot_1x2_close",
        animloop:
                可选，boolean，容器动画是否循环,如shadow_container的动画就是循环的
                开启时: 容器背景会持续播放动画效果,关闭时: 动画只播放一次就停止
        slotscale:
                可选，number，物品槽的统一缩放比例
                举例：
                    slotscale = 1.6,(弹弓的物品槽有缩放)
        slothighlightscale
                可选，number，物品槽的选中状态缩放比例
                鼠标悬停在槽位上时，槽位会进一步放大,一般会配合slotscale使用
        overrideactionfn:
                可选,函数，参数为inst和doer，覆盖默认的动作函数，返回值为bool，true表示已处理，不执行默认行为，返回false表示使用默认行为
                比如当玩家点击enable_shadow_rift_construction_container容器，不直接执行默认动作，而是先弹出确认对话框询问玩家是否确定，只有玩家点击"确定"后才执行实际操作
                举例：
                    function params.enable_shadow_rift_construction_container.widget.overrideactionfn(inst, doer)
                        if doer ~= nil and doer.HUD ~= nil and IsConstructionSiteComplete(inst, doer) then
                            -- 弹出确认对话框
                            local function EnableRiftsPopUpConfirm()
                                EnableRiftsDoAct(inst, doer)  -- 执行实际动作
                                TheFrontEnd:PopScreen()       -- 关闭对话框
                            end

                            local str = inst.POPUP_STRINGS
                            local confirmation = RiftConfirmScreen(str.TITLE, str.BODY, {
                                { text = str.OK,     cb = EnableRiftsPopUpConfirm },
                                { text = str.CANCEL, cb = EnableRiftsPopUpGoBack  },
                            })

                            TheFrontEnd:PushScreen(confirmation)  -- 显示确认对话框
                            return true  -- 返回true表示已处理，不执行默认行为
                        end
                        return false  -- 返回false表示使用默认行为
                    end
        
    },
    type： 
        必选，string，表示容器的类型，某些类型的容器显示的位置会有不同，默认是显示在屏幕中间，另外就是结合下面参数 openlimit 限制整个世界里只允许有几个当前类型的容器被打开
    issidewidget：
        可选，bool，标记容器是否显示在屏幕侧边而非中央，默认false
    openlimit：
        可选，number，限制整个世界里只允许有几个当前类型的容器被打开，没设定默认无限制
    acceptsstacks：
        可选，bool，标记容器是否接受堆叠物品的堆叠状态，比如能否将多个浆果放在一格里，默认true
    usespecificslotsforitems：
        可选，bool，启用槽位物品限制，不同槽位只能放置特定类型的物品，默认false
        这个东西有丶牛逼，影响的是快捷放入的逻辑，即后续的testfn中传入的slot为nil时的逻辑。
        默认不设置的情况下，假设在testfn中做了slot为1和2的检查，没做nil的检查，那么快捷放入后，可能会出现本来不应该被放入容器的物品被放入容器的情况（比如槽位1限制放入武器，但是选中武器shift快捷放入，武器还会进入槽位1）
        而一旦设置了该属性之后，快捷放入时会调用GetSpecificSlotForItem函数，该函数会将每个槽位都作为参数执行一遍testfn，直到找出符合要求的槽位，然后将武器放进去
    excludefromcrafting:
        可选，bool,决定容器内的物品是否参与合成材料计算，默认false
    lowpriorityselection:
        可选,bool,默认false,降低容器在物品选择时的优先级，避免意外取用
        当存在多个打开的容器时，游戏的容器选择算法如下：
                return containerwithsameitem         or  -- 1. 有相同物品且可堆叠的容器
                        containerwithemptyslot        or  -- 2. 有空槽位的普通容器  
                        containerwithnonstackableslot or  -- 3. 有空槽位的非堆叠容器
                        containerwithlowpirority          -- 4. 低优先级容器（最后考虑）
    itemtestfn:
        可选,函数，默认接受所有物品.
        函数参数:
            container:容器实例，
            item:待测试的物品
            slot:待测试的槽位,当使用shift+右键快捷放入时，slot为nil
        返回值：
            boolean（true表示可以放入
        举例：
            -- 冰箱 - 只接受易腐烂的食物
            function params.icebox.itemtestfn(container, item, slot)
                if item:HasTag("icebox_valid") then
                    return true  -- 特殊标记的物品
                end

                -- 必须是易腐烂的
                if not (item:HasTag("fresh") or item:HasTag("stale") or item:HasTag("spoiled")) then
                    return false
                end

                -- 不能是小动物
                if item:HasTag("smallcreature") then
                    return false
                end

                -- 必须是可食用的
                for k, v in pairs(FOODTYPE) do
                    if item:HasTag("edible_"..v) then
                        return true
                    end
                end

                return false
            end
    priorityfn:
        可选,函数,默认无特殊优先级，猜测是存在多个打开的容器时，快捷放入哪个容器的优先级
        函数参数:
            container:容器实例，
            item:待测试的物品
            slot:待测试的槽位,当使用shift+右键快捷放入时，slot为nil
        举例：
            -- 糖果袋 - 优先级函数
            function params.candybag.itemtestfn(container, item, slot)
                return item:HasTag("halloweencandy") or item:HasTag("halloween_ornament") or string.sub(item.prefab, 1, 8) == "trinket_"
            end
            params.candybag.priorityfn = params.candybag.itemtestfn  -- 糖果和饰品优先放入糖果袋
        后续补充:
            当把种子从箱子重使用shift快捷放置时，假设此时打开的容器有玩家物品栏以及A容器。
            如果容器没有设置priorityfn，那么系统会：
                1. 先检查背包中的种子堆叠，有的话则放入← 优先级高
                2. 再检查A容器中的种子堆叠，有的话则放入← 优先级低
                3.如果两个容器中都没有种子，先尝试背包空槽位
                4.再尝试A容器空槽位
            如果给A容器设置了priorityfn，且当item为种子时返回true：
                1.首先检查priorityfn对item是否为true，假设为true
                2.检查背包中的种子堆叠，但不立即返回，而是记录
                3.检查容器A中的种子堆叠
                4.如果两个容器中均不存在种子，则会直接先尝试A容器的空槽位
                5.再尝试背包空槽位
            PS：具体逻辑请查找官方的GetNextAvailableSlot函数
}
]]